package com.jialea.springboot.aspectj;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.jialea.springboot.annotation.RepeatSubmit;
import com.jialea.springboot.common.constants.CacheConstants;
import com.jialea.springboot.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;

/**
 * @author wf
 */
@Slf4j
@Aspect
@Component
public class RepeatSubmitAspect {
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Before("@annotation(repeatSubmit)")
    public void before(JoinPoint joinPoint, RepeatSubmit repeatSubmit) {
        try {
            //获取token
            String tokenValue = StpUtil.getTokenValue();
            //请求参数拼接
            String requestParams = argsArrayToString(joinPoint.getArgs());
            //缓存key
            String cacheKey = CacheConstants.REPEAT_SUBMIT_KEY + tokenValue + requestParams;
            if (Boolean.TRUE.equals(redisTemplate.hasKey(cacheKey))) {
                throw new BusinessException(repeatSubmit.message());
            }
            redisTemplate.opsForValue().set(cacheKey, "1", repeatSubmit.interval(), repeatSubmit.timeUnit());
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 参数拼接
     *
     * @param args 参数数组
     * @return 拼接后的字符串
     */
    private String argsArrayToString(Object[] args) {
        StringBuilder params = new StringBuilder();
        if (args != null && args.length > 0) {
            for (Object o : args) {
                if (Objects.nonNull(o) && !isFilterObject(o)) {
                    try {
                        params.append(JSONUtil.toJsonStr(o)).append(" ");
                    } catch (Exception e) {
                        log.error("参数拼接异常:{}", e.getMessage());
                    }
                }
            }
        }
        return params.toString().trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象
     * @return true：需要过滤；false：不需要过滤
     */
    private boolean isFilterObject(final Object o) {
        Class<?> c = o.getClass();
        //如果是数组且类型为文件类型的需要过滤
        if (c.isArray()) {
            return c.getComponentType().isAssignableFrom(MultipartFile.class);
        }
        //如果是集合且类型为文件类型的需要过滤
        else if (Collection.class.isAssignableFrom(c)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        }
        //如果是Map且类型为文件类型的需要过滤
        else if (Map.class.isAssignableFrom(c)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        //如果是文件类型的需要过滤
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }

}